/*
 *  Dir.c
 *  jscsh
 *
 *  Created by Jonathan Hohle on 1/9/08.
 *  Copyright 2008 hohle.net. All rights reserved.
 *
 */

#include "Dir.h"
#include "util.h"
#include <unistd.h>
#include <dirent.h>


void Dir_extendHost(JSContextRef context, JSObjectRef contextGlobalObject)
{
    JSStaticFunction staticFunctions[] = {
        {"chdir", Dir_s_chdir, kJSPropertyAttributeNone},
        {"chroot", Dir_s_chroot, kJSPropertyAttributeNone},
        {"entries", Dir_s_entries, kJSPropertyAttributeNone},
        {"mkdir", Dir_s_mkdir, kJSPropertyAttributeNone},
        {"pwd", Dir_s_pwd, kJSPropertyAttributeNone},
        {"rmdir", Dir_s_rmdir, kJSPropertyAttributeNone},
        {"tmpdir", Dir_s_tmpdir, kJSPropertyAttributeNone},
        {0, 0, 0}
    };
    
    JSClassDefinition Dir_s_definition = {
        0,
        kJSClassAttributeNone,
        
        "Dir",
        NULL,
        
        NULL,
        staticFunctions,
        
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL
    };
    
    JSClassRef Dir = JSClassCreate(&Dir_s_definition);
    JSObjectRef DirObject = JSObjectMake(context, Dir, NULL);
    JSStringRef DirString = JSStringCreateWithUTF8CString("Dir");
    JSObjectSetProperty(context,
                        contextGlobalObject,
                        DirString,
                        DirObject,
                        kJSPropertyAttributeNone,
                        NULL);
    JSClassRelease(Dir);
    JSStringRelease(DirString);
}

JSValueRef Dir_s_chdir(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    char* dir = NULL;
    char* p = getwd(NULL);
    
    if (argumentCount == 0)
    {
        dir = getenv("HOME");
    }
    else if (argumentCount > 0)
    {
        dir = UTF8CStringFromJSValueRef(context, arguments[0]);
    }
    
    int returnValue = chdir(dir);
    free(dir);
    
    if (returnValue != 0)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    if (argumentCount == 2)
    {
        if (JSValueIsObject(context, arguments[1]))
        {
            JSObjectRef functionRef = JSValueToObject(context,
                                                      arguments[1],
                                                      NULL);
            if (JSObjectIsFunction(context, functionRef))
            {
                JSStringRef pathRef = JSStringCreateWithUTF8CString(dir);
                JSValueRef pathValueRef = JSValueMakeString(context, pathRef);
                JSStringRelease(pathRef);
                JSValueRef arguments[] = {pathValueRef};
                JSObjectCallAsFunction(context,
                                       functionRef,
                                       thisObject,
                                       1,
                                       arguments,
                                       exception);
                chdir(p);
            }
        }
        else
        {
            *exception = JSValueMakeNumber(context, 1);

            return NULL;
        }
    }
    
    return JSValueMakeUndefined(context);
}


JSValueRef Dir_s_chroot(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* path = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    int returnValue = chroot(path);
    
    free(path);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeUndefined(context);
}

JSValueRef Dir_s_rmdir(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* path = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    int returnValue = rmdir(path);
    free(path);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeUndefined(context);
}

JSValueRef Dir_s_entries(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount != 1)
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    char* path = UTF8CStringFromJSValueRef(context, arguments[0]);
    
    DIR* directory = opendir(path);
    
    if (!directory)
    {
        *exception = JSValueMakeErrnoError(context, errno);
        return NULL;
    }
    
    JSObjectRef globalObject = JSContextGetGlobalObject(context);
    
    JSStringRef arrayString = JSStringCreateWithUTF8CString("Array");
    JSValueRef arrayRef = JSObjectGetProperty(context,
                                              globalObject,
                                              arrayString,
                                              NULL);
    JSObjectRef arrayConstructor = JSValueToObject(context,
                                                   arrayRef,
                                                   NULL);
    JSStringRelease(arrayString);
    JSValueRef array = JSObjectCallAsConstructor(context, 
                                                 arrayConstructor, 
                                                 0, 
                                                 NULL, 
                                                 NULL);
    JSObjectRef arrayObject = JSValueToObject(context,
                                              array,
                                              NULL);
    JSStringRef pushString = JSStringCreateWithUTF8CString("push");
    JSObjectRef pushMethod = JSValueToObject(context,
                                             JSObjectGetProperty(context,
                                                                 arrayObject,
                                                                 pushString,
                                                                 NULL),
                                             NULL);
    JSStringRelease(pushString);
    
    struct dirent* entry;
    
    while ((entry = readdir(directory)))
    {
        JSStringRef entryName = JSStringCreateWithUTF8CString(entry->d_name);
        JSValueRef entryValue = JSValueMakeString(context, entryName);
        JSStringRelease(entryName);
        JSValueRef arguments[] = {entryValue};
        
        JSObjectCallAsFunction(context,
                               pushMethod,
                               arrayObject,
                               1,
                               arguments,
                               exception);
    }
    
    closedir(directory);
    free(path);
    
    return array;
}


JSValueRef Dir_s_pwd(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    char* p = getwd(NULL);
    JSStringRef pathRef = JSStringCreateWithUTF8CString(p);
    JSValueRef pathValueRef = JSValueMakeString(context, pathRef);
    JSStringRelease(pathRef);
    free(p);
    return pathValueRef;
}

JSValueRef Dir_s_mkdir(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    if (argumentCount == 0 || argumentCount > 2 ||
        (argumentCount >= 1 && !JSValueIsString(context, arguments[0])) ||
        (argumentCount == 2 && !JSValueIsNumber(context, arguments[1])))
    {
        *exception = JSValueMakeNumber(context, 1);
        return NULL;
    }
    
    char* path = UTF8CStringFromJSValueRef(context, arguments[0]);
    mode_t mode = 0755;
    
    if (argumentCount == 2)
    {
        mode = (int) JSValueToNumber(context, arguments[2], NULL);
    }
    
    int returnValue = mkdir(path, mode);
    free(path);
    
    if (returnValue)
    {
        *exception = JSValueMakeErrnoError(context, returnValue);
        return NULL;
    }
    
    return JSValueMakeUndefined(context);
}

JSValueRef Dir_s_tmpdir(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    char* p = getenv("TMPDIR");
    JSStringRef pathRef = JSStringCreateWithUTF8CString(p);
    JSValueRef pathValueRef = JSValueMakeString(context, pathRef);
    JSStringRelease(pathRef);
    free(p);
    return pathValueRef;
}
